今天要介紹一個非常簡單好用的第三方套件EventBus,從有參加鐵人賽的念頭時就想說一定要寫它,恰好最近改版到3.0,現在寫可以跟前輩們的2.X介紹文有點區隔,完美。
今天的練習會跟昨天的RecyclerView結合,當我們點選時把項目名稱交給Activity更新主畫面。
EventBus是一個觀察者模式的套件,常見的用途是「跨程式溝通」
(來自官方Github)
例如在Activity註冊一個觀察者(Subscriber),之後在其他程式用發佈者(Publisher)發出訊息,Activity就會收到訊息,我們再依訊息做相應動作。
看完以上還是不懂沒關係,我當初看也沒有懂,待會看程式碼會更了解運作方式,並發現它真的很簡單!
在gradle加入:
compile 'org.greenrobot:eventbus:3.0.0'
忘記怎麼加入的可以參考Day13有圖解
Event可以是任意class,我們建個簡單的MessageEvent,裡面帶有一個字串
public class MessageEvent {
private String Message;
public MessageEvent(String message) {
this.Message = message;
}
public String getMessage() {
return Message;
}
}
一般在Activity中註冊會像這樣
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onStart() {
super.onStart();
// 在此Activity啟用EventBus
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
// 在Activity停用EventBus,讓Subscribe停止接收
EventBus.getDefault().unregister(this);
}
// 註冊Subscribe,觀察目標為MessageEvent
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
// 收到MessageEvent時要做的事寫在這裡
};
}
在onStart開始觀察,並在onStop停止觀察,因onStop時Activity已經不在畫面上了,停止觀察可以避免多餘的運作,可以參考Day11 - Activity生命週期
只要一行程式,不需要任何準備動作
EventBus.getDefault().post(new MessageEvent("要發送的訊息"));
當這行程式執行時,所有觀察目標為MessageEvent的Subscribe就會收到訊息了,接著就把EventBus加入昨天的專案中
延續昨天的專案,若我們點擊列表時想在主畫面用TextView顯示出項目名稱,此時會發現點擊事件寫在MyAdapter裡,而要更新的TextView卻在MainActivity,要怎麼讓這兩個程式溝通就可以靠EventBus來實現
將publisher放到ViewHolder點擊事件裡
class ViewHolder extends RecyclerView.ViewHolder{
ViewHolder(View itemView) {
super(itemView);
// 略..
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 取得項目名稱
String msg = mData.get(getAdapterPosition());
// 發出MessageEvent
EventBus.getDefault().post(new MessageEvent(msg));
}
});
}
}
activity_main新增一個TextView用來顯示收到的訊息
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"/>
<Button
android:id="@+id/btnAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/txtTitle"
android:text="新增"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/btnAdd"/>
</RelativeLayout>
MainActivity的Subscribe中更新TextView
public class MainActivity extends AppCompatActivity {
private TextView txtTitle;
// 略..
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtTitle = (TextView) findViewById(R.id.txtTitle);
//略..
}
@Override
public void onStart() {
super.onStart();
// 在此Activity啟用EventBus
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
// 在Activity停用EventBus,讓Subscribe停止接收
EventBus.getDefault().unregister(this);
}
// 註冊Subscribe,觀察目標為MessageEvent
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
// 收到MessageEvent時將內容顯示出來
txtTitle.setText("選擇的是:" + event.getMessage());
};
完成,點選項目時ViewHolder會發出MessageEvent,Activity收到並將內容顯示出來,就完成跨程式的溝通囉
EventBus使用上非常簡潔,除了Reddit討論串建議如本篇的方式和RecyclerView搭配使用之外,將來若有開發到Fragment或Service要與Activity互相溝通時更是出奇的好用,建議大家都可以試用看看哦。
使用EventBus來做RecyclerView點擊事件是不好的方式,一對一的程式互動應盡量使用interface等方式來處理,需要一對多時才考慮EventBus,因為EventBus散落在專案各處將使專案擴大後難以維護。interface的建立方式請參考昨天文章的結尾處。